package com.ll19.dao.jpa;

import java.util.Collection;

import javax.persistence.EntityManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.Version;
import org.hibernate.search.MassIndexer;
import org.hibernate.search.SearchFactory;
import org.hibernate.search.indexes.IndexReaderAccessor;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;

/**
 * Utility class to generate lucene queries for hibernate search and perform
 * full reindexing.
 * 
 * @author jgarcia
 */
public class HibernateSearchJpaTools {
	protected static final Log log = LogFactory
			.getLog(HibernateSearchJpaTools.class);

	/**
	 * Generates a lucene query to search for a given term in all the indexed
	 * fields of a class
	 * 
	 * @param searchTerm
	 *            the term to search for
	 * @param searchedEntity
	 *            the class searched
	 * @param entityManager
	 *            the entity manager
	 * @param defaultAnalyzer
	 *            the default analyzer for parsing the search terms
	 * @return
	 * @throws ParseException
	 */
	public static Query generateQuery(String searchTerm, Class searchedEntity,
			EntityManager entityManager, Analyzer defaultAnalyzer)
			throws ParseException {
		Query qry = null;

		if (searchTerm.equals("*")) {
			qry = new MatchAllDocsQuery();
		} else {
			// Search in all indexed fields

			IndexReaderAccessor readerAccessor = null;
			IndexReader reader = null;
			try {
				FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search
						.getFullTextEntityManager(entityManager);

				// obtain analyzer to parse the query:
				Analyzer analyzer;
				if (searchedEntity == null) {
					analyzer = defaultAnalyzer;
				} else {
					analyzer = fullTextEntityManager.getSearchFactory()
							.getAnalyzer(searchedEntity);
				}

				// search on all indexed fields: generate field list, removing
				// internal hibernate search field name: _hibernate_class
				// TODO: possible improvement: cache the fields of each entity
				SearchFactory searchFactory = fullTextEntityManager
						.getSearchFactory();
				readerAccessor = searchFactory.getIndexReaderAccessor();
				reader = readerAccessor.open(searchedEntity);
				Collection<String> fieldNames = reader
						.getFieldNames(IndexReader.FieldOption.INDEXED);
				fieldNames.remove("_hibernate_class");
				String[] fnames = new String[0];
				fnames = fieldNames.toArray(fnames);

				// To search on all fields, search the term in all fields
				String[] queries = new String[fnames.length];
				for (int i = 0; i < queries.length; ++i) {
					queries[i] = searchTerm;
				}

				qry = MultiFieldQueryParser.parse(Version.LUCENE_35, queries,
						fnames, analyzer);
			} finally {
				if (readerAccessor != null && reader != null) {
					readerAccessor.close(reader);
				}
			}
		}
		return qry;
	}

	/**
	 * Regenerates the index for a given class
	 * 
	 * @param clazz
	 *            the class
	 * @param entityManager
	 *            the entity manager
	 */
	public static void reindex(Class clazz, EntityManager entityManager) {
		FullTextEntityManager txtentityManager = Search
				.getFullTextEntityManager(entityManager);
		MassIndexer massIndexer = txtentityManager.createIndexer(clazz);
		try {
			massIndexer.startAndWait();
		} catch (InterruptedException e) {
			log.error("mass reindexing interrupted: " + e.getMessage());
		} finally {
			//flushToIndexes是否和更新操作的flush一样？只是将内存的信息更新至索引保证同步  而不是把数据库所有的信息都更新成索引？
			txtentityManager.flushToIndexes();
		}
	}

	/**
	 * Regenerates all the indexed class indexes
	 * 
	 * @param async
	 *            true if the reindexing will be done as a background thread
	 * @param entityManager
	 *            the entity manager
	 */
	public static void reindexAll(boolean async, EntityManager entityManager) {
		FullTextEntityManager txtentityManager = Search
				.getFullTextEntityManager(entityManager);
		MassIndexer massIndexer = txtentityManager.createIndexer();
		massIndexer.purgeAllOnStart(true);
		try {
			if (!async) {
				massIndexer.startAndWait();
			} else {
				massIndexer.start();
			}
		} catch (InterruptedException e) {
			log.error("mass reindexing interrupted: " + e.getMessage());
		} finally {
			//flushToIndexes是否和更新操作的flush一样？只是将内存的信息更新至索引保证同步  而不是把数据库所有的信息都更新成索引？
			txtentityManager.flushToIndexes();
		}
	}
}
